/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.HashMap;
import java.util.Map;

class StringFasta {
    public static int last = 42;
    public static int ret = 0;
    public static final int A = 3877;
    public static final int C = 29573;
    public static final int M = 139968;
    public static final int TOBASE_NUMBER_60 = 60;
    public static final double TOBASE_NUMBER_0_27 = 0.27;
    public static final double TOBASE_NUMBER_0_12 = 0.12;
    public static final double TOBASE_NUMBER_0_02 = 0.02;
    public static final double TOBASE_NUMBER_0_3029549426680 = 0.302954942668;
    public static final double TOBASE_NUMBER_0_1979883004921 = 0.1979883004921;
    public static final double TOBASE_NUMBER_0_1975473066391 = 0.1975473066391;
    public static final double TOBASE_NUMBER_0_3015094502008 = 0.3015094502008;
    public static final int TOBASE_NUMBER_100000 = 100000;
    public static final int TOBASE_NUMBER_3 = 3;
    public static final int TOBASE_NUMBER_2 = 2;
    public static final int TOBASE_NUMBER_5 = 5;
    public static final int TOBASE_NUMBER_1000 = 1000;

    public static double rand(double max) {
        last = (last * A + C) % M;
        return (max * last) / M;
    }

    public static void makeCumulative(Map<String, Double> table) {
        String last = null;
        for (String key : table.keySet()) {
            if (last != null) {
                table.put(key, table.get(key) + table.get(last));
            }
            last = key;
        }
    }

    public static void fastaRepeat(int m, String seq) {
        int n = m;
        int seqi = 0;
        int lenOut = 60;
        while (n > 0) {
            if (n < lenOut) {
                lenOut = n;
            }
            if (seqi + lenOut < seq.length()) {
                ret += lenOut;
                seqi += lenOut;
            } else {
                String s = seq.substring(seqi);
                seqi = lenOut - s.length();
                ret += (s + seq.substring(0, seqi)).length();
            }
            n -= lenOut;
        }
    }

    public static void fastaRandom(int m, Map<String, Double> table) {
        int n = m;
        String[] line = new String[TOBASE_NUMBER_60];
        makeCumulative(table);
        while (n > 0) {
            if (n < line.length) {
                line = new String[n];
            }
            for (int i = 0; i < line.length; i++) {
                int r = (int) rand(1);
                for (Map.Entry<String, Double> item : table.entrySet()) {
                    if (r < item.getValue()) {
                        line[i] = item.getKey();
                        break;
                    }
                }
            }
            ret += line.length;
            n -= line.length;
        }
    }
}

/*
 * @State
 * @Tags Jetstream2
 *
 */
class Benchmark {
    public static void main(String[] args) {
        double start = (double) System.nanoTime() / 1_000_000;
        runIteration();
        double end = (double) System.nanoTime() / 1_000_000;
        System.out.println("string-fasta: ms = " + (end - start));
    }
    /*
     *  @Benchmark
     */
    public static void runIteration() {
        final int count = 7;

        String alu =
                "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" +
                        "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" +
                        "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" +
                        "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" +
                        "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" +
                        "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" +
                        "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";

        Map<String, Double> iub = new HashMap<>();
        iub.put("a", StringFasta.TOBASE_NUMBER_0_27);
        iub.put("c", StringFasta.TOBASE_NUMBER_0_12);
        iub.put("g", StringFasta.TOBASE_NUMBER_0_12);
        iub.put("t", StringFasta.TOBASE_NUMBER_0_27);
        iub.put("B", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("D", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("H", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("K", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("M", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("N", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("R", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("S", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("V", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("W", StringFasta.TOBASE_NUMBER_0_02);
        iub.put("Y", StringFasta.TOBASE_NUMBER_0_02);

        Map<String, Double> homoSap = new HashMap<>();
        homoSap.put("a", StringFasta.TOBASE_NUMBER_0_3029549426680);
        homoSap.put("c", StringFasta.TOBASE_NUMBER_0_1979883004921);
        homoSap.put("g", StringFasta.TOBASE_NUMBER_0_1975473066391);
        homoSap.put("t", StringFasta.TOBASE_NUMBER_0_3015094502008);

        StringFasta.fastaRepeat(StringFasta.TOBASE_NUMBER_2 * count * StringFasta.TOBASE_NUMBER_100000, alu);
        StringFasta.fastaRandom(StringFasta.TOBASE_NUMBER_3 * count * StringFasta.TOBASE_NUMBER_1000, iub);
        StringFasta.fastaRandom(StringFasta.TOBASE_NUMBER_5 * count * StringFasta.TOBASE_NUMBER_1000, homoSap);

        int expected = 1456000;
        if (StringFasta.ret != expected) {
            throw new RuntimeException("ERROR: bad result: expected " + expected + " but got " + StringFasta.ret);
        }else {
//            System.out.println("string-fasta: result is equal to expected");
        }
    }
}